home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / cvs-1.8 / cvs-1 / cvs-1.8.1 / src / login.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  10.5 KB  |  393 lines

  1. /*
  2.  * Copyright (c) 1995, Cyclic Software, Bloomington, IN, USA
  3.  * 
  4.  * You may distribute under the terms of the GNU General Public License as
  5.  * specified in the README file that comes with CVS.
  6.  * 
  7.  * Allow user to log in for an authenticating server.
  8.  */
  9.  
  10. #include "cvs.h"
  11. #include "getline.h"
  12.  
  13. #ifdef AUTH_CLIENT_SUPPORT   /* This covers the rest of the file. */
  14.  
  15. extern char *getpass ();
  16.  
  17. #ifndef CVS_PASSWORD_FILE 
  18. #define CVS_PASSWORD_FILE ".cvspass"
  19. #endif
  20.  
  21. /* If non-NULL, get_cvs_password() will just return this. */
  22. static char *cvs_password = NULL;
  23.  
  24. /* The return value will need to be freed. */
  25. char *
  26. construct_cvspass_filename ()
  27. {
  28.   char *homedir;
  29.   char *passfile;
  30.  
  31.   /* Environment should override file. */
  32.   if ((passfile = getenv ("CVS_PASSFILE")) != NULL)
  33.     return xstrdup (passfile);
  34.  
  35.   /* Construct absolute pathname to user's password file. */
  36.   /* todo: does this work under OS/2 ? */
  37.   homedir = get_homedir ();
  38.   if (! homedir)
  39.     {
  40.       error (1, errno, "could not find out home directory");
  41.       return (char *) NULL;
  42.     }
  43.   
  44.   passfile =
  45.     (char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3);
  46.   strcpy (passfile, homedir);
  47.   strcat (passfile, "/");
  48.   strcat (passfile, CVS_PASSWORD_FILE);
  49.   
  50.   /* Safety first and last, Scouts. */
  51.   if (isfile (passfile))
  52.     /* xchmod() is too polite. */
  53.     chmod (passfile, 0600);
  54.  
  55.   return passfile;
  56. }
  57.  
  58.  
  59. /* Prompt for a password, and store it in the file "CVS/.cvspass".
  60.  *
  61.  * Because the user might be accessing multiple repositories, with
  62.  * different passwords for each one, the format of ~/.cvspass is:
  63.  *
  64.  * user@host:/path Acleartext_password
  65.  * user@host:/path Acleartext_password
  66.  * ...
  67.  *
  68.  * Of course, the "user@" might be left off -- it's just based on the
  69.  * value of CVSroot.
  70.  *
  71.  * The "A" before "cleartext_password" is a literal capital A.  It's a
  72.  * version number indicating which form of scrambling we're doing on
  73.  * the password -- someday we might provide something more secure than
  74.  * the trivial encoding we do now, and when that day comes, it would
  75.  * be nice to remain backward-compatible.
  76.  *
  77.  * Like .netrc, the file's permissions are the only thing preventing
  78.  * it from being read by others.  Unlike .netrc, we will not be
  79.  * fascist about it, at most issuing a warning, and never refusing to
  80.  * work.
  81.  */
  82. int
  83. login (argc, argv)
  84.     int argc;
  85.     char **argv;
  86. {
  87.   char *passfile;
  88.   FILE *fp;
  89.   char *typed_password, *found_password;
  90.   char *linebuf = (char *) NULL;
  91.   size_t linebuf_len;
  92.   int root_len, already_entered = 0;
  93.  
  94.   /* Make this a "fully-qualified" CVSroot if necessary. */
  95.   if (! strchr (CVSroot, '@'))
  96.     {
  97.       /* We need to prepend "user@host:". */
  98.       char *tmp;
  99.  
  100.       printf ("Repository \"%s\" not fully-qualified.\n", CVSroot);
  101.       printf ("Please enter \"user@host:/path\": ");
  102.       fflush (stdout);
  103.       getline (&linebuf, &linebuf_len, stdin);
  104.  
  105.       tmp = xmalloc (strlen (linebuf) + 1);
  106.  
  107.       /* Give it some permanent storage. */
  108.       strcpy (tmp, linebuf);
  109.       tmp[strlen (linebuf) - 1] = '\0';
  110.       CVSroot = tmp;
  111.  
  112.       /* Reset. */
  113.       free (linebuf);
  114.       linebuf = (char *) NULL;
  115.     }
  116.  
  117.   if (CVSroot[0] != ':')
  118.     {
  119.       /* Then we need to prepend ":pserver:". */
  120.       char *tmp;
  121.  
  122.       tmp = xmalloc (strlen (":pserver:") + strlen (CVSroot) + 1);
  123.       strcpy (tmp, ":pserver:");
  124.       strcat (tmp, CVSroot);
  125.       CVSroot = tmp;
  126.     }
  127.  
  128.   /* Check to make sure it's fully-qualified before going on. 
  129.    * Fully qualified in this context means it has both a user and a
  130.    * host:repos portion.
  131.    */
  132.   {
  133.     char *r;
  134.  
  135.     /* After confirming that CVSroot is non-NULL, we skip past the
  136.        initial ":pserver:" to test the rest of it. */
  137.  
  138.     if (! CVSroot)
  139.       error (1, 0, "CVSroot is NULL");
  140.     else if (! strchr ((r = (CVSroot + strlen (":pserver:"))), '@'))
  141.       goto not_fqrn;
  142.     else if (! strchr (r, ':'))
  143.       goto not_fqrn;
  144.     
  145.     if (0)        /* Lovely. */
  146.       {
  147.       not_fqrn:
  148.         error (0, 0, "CVSroot not fully-qualified: %s", CVSroot);
  149.         error (1, 0, "should be format user@host:/path/to/repository");
  150.       }
  151.   }
  152.     
  153.   /* CVSroot is now fully qualified and has ":pserver:" prepended.
  154.      We'll print out most of it so user knows exactly what is being
  155.      dealt with here. */
  156.   {
  157.     char *s;
  158.     s = strchr (CVSroot, ':');
  159.     s++;
  160.     s = strchr (s, ':');
  161.     s++;
  162.  
  163.     if (s == NULL)
  164.       error (1, 0, "NULL CVSroot");
  165.  
  166.     printf ("(Logging in to %s)\n", s);
  167.     fflush (stdout);
  168.   }
  169.  
  170.   passfile = construct_cvspass_filename ();
  171.   typed_password = getpass ("CVS password: ");
  172.   typed_password = scramble (typed_password);
  173.  
  174.   /* Force get_cvs_password() to use this one (when the client
  175.    * confirms the new password with the server), instead of consulting
  176.    * the file.  We make a new copy because cvs_password will get
  177.    * zeroed by connect_to_server().
  178.    */
  179.   cvs_password = xstrdup (typed_password);
  180.  
  181.   if (connect_to_pserver (NULL, NULL, 1) == 0)
  182.     {
  183.       /* The password is wrong, according to the server. */
  184.       error (1, 0, "incorrect password");
  185.     }
  186.  
  187.   /* IF we have a password for this "[user@]host:/path" already
  188.    *  THEN
  189.    *    IF it's the same as the password we read from the prompt
  190.    *     THEN 
  191.    *       do nothing
  192.    *     ELSE
  193.    *       replace the old password with the new one
  194.    *  ELSE
  195.    *    append new entry to the end of the file.
  196.    */
  197.  
  198.   root_len = strlen (CVSroot);
  199.  
  200.   /* Yes, the method below reads the user's password file twice.  It's
  201.      inefficient, but we're not talking about a gig of data here. */
  202.  
  203.   fp = fopen (passfile, "r");
  204.   /* FIXME: should be printing a message if fp == NULL and not
  205.      existence_error (errno).  */
  206.   if (fp != NULL)
  207.     {
  208.       /* Check each line to see if we have this entry already. */
  209.       while (getline (&linebuf, &linebuf_len, fp) >= 0)
  210.         {
  211.           if (strncmp (CVSroot, linebuf, root_len) == 0)
  212.             {
  213.               already_entered = 1;
  214.               break;
  215.             }
  216.           else
  217.             {
  218.               free (linebuf);
  219.               linebuf = (char *) NULL;
  220.             }
  221.         }
  222.       fclose (fp);
  223.     }
  224.       
  225.   if (already_entered)
  226.     {
  227.       /* This user/host has a password in the file already. */
  228.  
  229.       strtok (linebuf, " ");
  230.       found_password = strtok (NULL, "\n");
  231.       if (strcmp (found_password, typed_password))
  232.         {
  233.           /* typed_password and found_password don't match, so we'll
  234.            * have to update passfile.  We replace the old password
  235.            * with the new one by writing a tmp file whose contents are
  236.            * exactly the same as passfile except that this one entry
  237.            * gets typed_password instead of found_password.  Then we
  238.            * rename the tmp file on top of passfile.
  239.            */
  240.           char *tmp_name;
  241.           FILE *tmp_fp;
  242.  
  243.           tmp_name = tmpnam (NULL);
  244.           if ((tmp_fp = fopen (tmp_name, "w")) == NULL)
  245.             {
  246.               error (1, errno, "unable to open temp file %s", tmp_name);
  247.               return 1;
  248.             }
  249.           chmod (tmp_name, 0600);
  250.  
  251.           fp = fopen (passfile, "r");
  252.           if (fp == NULL)
  253.             {
  254.               error (1, errno, "unable to open %s", passfile);
  255.               return 1;
  256.             }
  257.           /* I'm not paranoid, they really ARE out to get me: */
  258.           chmod (passfile, 0600);
  259.  
  260.           free (linebuf);
  261.           linebuf = (char *) NULL;
  262.           while (getline (&linebuf, &linebuf_len, fp) >= 0)
  263.             {
  264.               if (strncmp (CVSroot, linebuf, root_len))
  265.                 fprintf (tmp_fp, "%s", linebuf);
  266.               else
  267.                 fprintf (tmp_fp, "%s %s\n", CVSroot, typed_password);
  268.  
  269.               free (linebuf);
  270.               linebuf = (char *) NULL;
  271.             }
  272.           fclose (tmp_fp);
  273.           fclose (fp);
  274.           rename_file (tmp_name, passfile);
  275.           chmod (passfile, 0600);
  276.         }
  277.     }
  278.   else
  279.     {
  280.       if ((fp = fopen (passfile, "a")) == NULL)
  281.         {
  282.           error (1, errno, "could not open %s", passfile);
  283.           free (passfile);
  284.           return 1;
  285.         }
  286.  
  287.       fprintf (fp, "%s %s\n", CVSroot, typed_password);
  288.       fclose (fp);
  289.     }
  290.  
  291.   /* Utter, total, raving paranoia, I know. */
  292.   chmod (passfile, 0600);
  293.   memset (typed_password, 0, strlen (typed_password));
  294.   free (typed_password);
  295.  
  296.   free (passfile);
  297.   free (cvs_password);
  298.   cvs_password = NULL;
  299.   return 0;
  300. }
  301.  
  302. /* todo: "cvs logout" could erase an entry from the file.
  303.  * But to what purpose?
  304.  */
  305.  
  306. /* Returns the _scrambled_ password.  The server must descramble
  307.    before hashing and comparing. */
  308. char *
  309. get_cvs_password ()
  310. {
  311.   int found_it = 0;
  312.   int root_len;
  313.   char *password;
  314.   char *linebuf = (char *) NULL;
  315.   size_t linebuf_len;
  316.   FILE *fp;
  317.   char *passfile;
  318.  
  319.   /* If someone (i.e., login()) is calling connect_to_pserver() out of
  320.      context, then assume they have supplied the correct, scrambled
  321.      password. */
  322.   if (cvs_password)
  323.     return cvs_password;
  324.  
  325.   /* Environment should override file. */
  326.   if ((password = getenv ("CVS_PASSWORD")) != NULL)
  327.     {
  328.       char *p;
  329.       p = xstrdup (password);
  330.       /* If we got it from the environment, then it wasn't properly
  331.          scrambled.  Since unscrambling is done on the server side, we
  332.          need to transmit it scrambled. */
  333.       p = scramble (p);
  334.       return p;
  335.     }
  336.  
  337.   /* Else get it from the file. */
  338.   passfile = construct_cvspass_filename ();
  339.   fp = fopen (passfile, "r");
  340.   if (fp == NULL)
  341.     {
  342.       error (0, errno, "could not open %s", passfile);
  343.       free (passfile);
  344.       error (1, 0, "use \"cvs login\" to log in first");
  345.     }
  346.  
  347.   root_len = strlen (CVSroot);
  348.  
  349.   /* Check each line to see if we have this entry already. */
  350.   while (getline (&linebuf, &linebuf_len, fp) >= 0)
  351.     {
  352.       if (strncmp (CVSroot, linebuf, root_len) == 0)
  353.         {
  354.           /* This is it!  So break out and deal with linebuf. */
  355.           found_it = 1;
  356.           break;
  357.         }
  358.       else
  359.         {
  360.           free (linebuf);
  361.           linebuf = (char *) NULL;
  362.         }
  363.     }
  364.  
  365.   if (found_it)
  366.     {
  367.       /* linebuf now contains the line with the password. */
  368.       char *tmp;
  369.       
  370.       strtok (linebuf, " ");
  371.       password = strtok (NULL, "\n");
  372.       
  373.       /* Give it permanent storage. */
  374.       tmp = xmalloc (strlen (password) + 1);
  375.       strcpy (tmp, password);
  376.       tmp[strlen (password)] = '\0';
  377.       memset (password, 0, strlen (password));
  378.       free (linebuf);
  379.       return tmp;
  380.     }
  381.   else
  382.     {
  383.       error (0, 0, "cannot find password");
  384.       error (0, 0, "use \"cvs login\" to log in first");
  385.       error (1, 0, "or set the CVS_PASSWORD environment variable");
  386.     }
  387.   /* NOTREACHED */
  388.   return NULL;
  389. }
  390.  
  391. #endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
  392.  
  393.